iT邦幫忙

2022 iThome 鐵人賽

DAY 30
0

測試這件事情是為了一致性,為了不讓伴侶不信任,在男女之間交往一致性也是很重要的一環。

在日常的對話和相處過程中,彼此都會透過些方法來確認, John Rempel 等人提出信任包含可預測、可依賴和信念三個要素。

  • 可預測判斷的依據就是過去伴侶行為的一致性
  • 可依賴代表相信且願意依賴伴侶協助解決問題

以程式測試來說,每次的測試會期待能得到相同結果,並且認為能夠依賴系統解決問題。

舉個例子來說小編前公司是做看盤軟體,為了確保 Production 環境穩定,每天會在開盤前執行 E2E 測試確保網站運作正常,即便當錯誤發生也能即時寄信通知相關人員。

JavaScript 測試

寫測試前要問三個問題

  1. 在什麼情境?
  2. 要測試什麼?
  3. 預期的結果?

測試的步驟上大致會遵守 3A 原則

  1. Arrange: 準備物件、建立物件、物件設定 (fixture) -> 情境
  2. Act: 操作物件 -> 測試內容
  3. Assert: 驗證結果符合預期 -> 預期結果

好的單元測試

  • 執行夠快
  • 會得到相同結果
  • 跟其他測試完全獨立
  • 理論上不需要 DB、網路,用 Mock Server 或是 Mock Object 模擬外部傳回資料

測試是為了開發速度和產品品質,不要因為著急而跳過測試,沒有測試的話會花更多時間在上線前的來回,可能有些問題還沒修好就上線,引發更多新問題,但什麼都要測試嗎?

  • 追求 "測到關鍵邏輯",關鍵的測試有測到更重要
  • 測試是為了速度與品質
  • 不好的 "快" 不是 "真的快"
  • 寫測試的時候應避免重複寫原來的程式邏輯

建議做黑箱測試也就是測試流程與結果即可,在測試公開的介面或元件的過程中其實也會順便測試到內部邏輯,當撰寫 E2E 測試的時候也有單元測試覆蓋的概念,雖然 E2E 的運行成本較高,但針對可能會頻繁更改的內部實作做單元測試也有點浪費時間。

另外 E2E 測試也可以當作一個文件,可以告訴不熟系統的人關鍵操作流程以及定義什麼是正常的操作,按照使用者故事跑過網站的關鍵流程,並且交由助理工程師來協助驗收。

推薦工具:

  • BDD (行為驅動測試) 框架是 Cucumber 可以參考看看
  • StoryBook 提供一個元件操作介面來做元件測試

單元測試

當前端開發元件化以後,最基本的就是針對元件做單元測試,第一次寫測試的話可以先問自己幾個問題:

  • 單元測試該怎麼撰寫?
  • Jest 的腳色是什麼?
  • 什麼是 enzyme?
  • 什麼是 shallow & mount ?

單元測試通常是一個自動化的測試,確保某一段程式碼 (單元) 有被正式執行,在測試時大多使用單元測試框架測試。

  • 會呼叫被測試單元的入口點
  • 檢查其中一個出口點,所以唯一出口較好測試

單元測試在測什麼

  • 元件測試,import 後用 mount 然後 props 用假資料進行測試,模擬點擊 jest.fn()
  • function 測試,import 後用假資料測試
  • 非同步: jest.fn()
  • test fixture: 測試的時候特意準備的東西,讓測試可以順利跑完所需要,有些人會叫做 test context
  • 包含測試
  • 出口測試
    • CUT: Code under test
    • SUT: system under test

依照使用的框架或函式庫不同會有不同的測試工具,像 react 的話 facebook 官方就有 Jest

Jest 對於單元測試來說非常方便而且也包含了覆蓋率的計算,最後會直接出一個報表給你,文章中也有推薦 enzyme,是 airbnb 開發的工具,據 react 官方說法是讓測試更簡單。

enzyme 只是讓我們更方便測試 react 用的外掛,主要是因為 react 有用到 virtual dom ,那測試又會需要去 render,所以 enzyme 就封裝了 react 原生的測試讓寫法更直覺。

  • shallow: 只 render 第一層
  • mount: full render,包含元件週期
  • render
  • simulate: 模擬 event
  • setProps: 設定 props
  • setState: 設定 state
  • prop(key): retrieves prop value corresponding to the provided key
  • state(key): retrieves state corresponding to the provided key

單元測試該怎麼撰寫

寫法上會用到以下三個基本關鍵字 describe , it , expect,這是撰寫單元測試的語法架構,寫完這三項就是基本的測試了。

  • describe: 主要是拿來整理 it 用(在這邊又可以用 test 代替),相關的 it 就可以包在 describe 裡面作更進一步的分類整理。
  • it: 是最小的測試單位,所以每一項測試都要寫在某個 it function 裡。it 第一個參數是測試名稱,其實就是給你註解到底這個測試要幹嘛,然後搭配 before、beforeEach、after、afterEach 做進階操作
  • expect: 按照名字來看就是你預期他到底怎樣,搭配 it 這個測試項目,看跑出來的結果是不是跟 expect 中的一樣,這就是基本的測試了
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import sum from "./sum";

describe("測試群組一", () => {
  it("sums numbers", () => {
    expect(sum(1, 2)).toEqual(3);
    expect(sum(2, 2)).toEqual(4);
  });

  it("renders without crashing", () => {
    const div = document.createElement("div");
    ReactDOM.render(<App />, div);
  });
});

E2E 測試

Cypress 提供針對測試的配置、撰寫、執行、除錯完整的 end-to-end test 的解決方案,比較特別的是 Cypress 也像 Redux 的專案一樣提供了時空旅行的功能,並且提供了方便的介面讓我們更容易去針對測試進行除錯。

這次就用貓貓點擊大賽參戰的範例來帶大家超快速入門,來看看怎麼讓機器來取代 Popcat 的人工點擊!!!

原始碼: https://github.dev/LinYenCheng/popcat-cypress

Cypress 在使用上其實也很簡單,只要透過簡單的安裝設定就能夠直接執行並撰寫測試了,不一定要測試自己開發的網站,針對網路上的任何站台都能夠執行測試腳本。

Cypress 安裝設定

如果是 npm 的專案

npm install cypress --save-dev

首先要設定 npm script

{
  "name": "popcat",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "cypress open",
    "test-one": "cypress run --spec **/popcat.spec.js"
  },
  "author": "",
  "license": "ISC"
}

安裝完完並設定之後,雖然專案資料夾都是全空的,但其實就可以執行了,這時候 Cypress 會偵測到我們是第一次執行,會自動將相關配置及範例加入。

第一次執行 cypress open > firstTime

接著 Cypress 會自動加入相關預設的資料夾配置如下

  • fixtures: 存放假資料或是常數值
  • integration: 測試撰寫的地方
  • plugins: 沒有想要修改預設功能一般不會用到
  • support
    • commands: 擴充共用的函式可以統一放在這邊

第一次啟動後的畫面
sampleTest

此外其實若想修改預設配置,也可以透過設定 cypress.json 來達到相關效果,底下是基本的範例可以看出我們能多設定像是重試次數、影片錄製等等參數,設定檔相關說明可以參考設定檔的參考文件

{
  "viewportWidth": 411,
  "viewportHeight": 731,
  "defaultCommandTimeout": 8000,
  "requestTimeout": 8000,
  "retries": 8,
  "video": true,
  "videoUploadOnPasses": false,
  "chromeWebSecurity": false,
  "reporter": "junit",
  "ignoreTestFiles": "**/examples/*.js",
  "reporterOptions": {
    "mochaFile": "results/TEST-[hash].xml"
  }
}

Cypress 測試撰寫

Popcat 貓貓點擊大賽參戰確認!!! 一個最簡單的測試腳本如下:

context("popcat.spec", () => {
  beforeEach(() => {
    Cypress.on("uncaught:exception", (err, runnable) => {
      return false;
    });
    cy.visit("https://popcat.click/");
  });

  describe("貓貓點擊大賽", () => {
    it("點 1000 下", () => {
      Array(1000)
        .fill("val")
        .forEach((elm, index) => {
          cy.wait(100 * ((index % 5) + 1));
          cy.get(".cat-img").click();
        });
    });
  });
});
  • Cypress visit(): cy.visit() 模擬瀏覽器開啟網站
  • Cypress wait(): cy.wait() 模擬等待
  • Cypress get(): cy.get() 類似 jQuery 的選擇器,可以協助我們找出網頁元素

學前端這麼多年,第一次覺得派上用場 ? 在台灣,每秒鐘都有上萬隻貓正在張開嘴巴。

popcat70k

  1. 分析 DOM

這個部分其實就會使用到 Chrome 的開發者元件,透過檢視,我們可以發現主要的網站很單純沒有太多設計,我們就只要針對 .cat-img 這個 class 進行模擬點擊就可以了,甚至是沒有針對直接對整個網頁觸發鍵盤事件也會有效果。

  1. 觸發事件

參戰最簡單的方法其實就是透過 console 來執行,不過這次是想透過測試工具 XD 至於如何使用 Chrome 開發者工具歡迎參考之前寫過的文章,相信可以快速入門的。

var event = new KeyboardEvent("keydown", {
  key: "t",
  ctrlKey: true,
});

setInterval(function () {
  document.dispatchEvent(event);
}, 500);
  1. 觀察 API

可以發現是 API 主要步驟有兩個

  • 透過 reload 來換 token
  • 累積次數一陣子後透過 POP 這個 API 集中將累積次數 (pop_count) 送到後端

相關 API
chromeDevToolNetworkTab

如果短時間打太頻繁其實也是會被阻擋:

提醒大家運動家精神 XD
TooManyRequest

重複的執行也是會得到 403 的錯誤,這時候需要把整個測試關掉重開。

403

Cypress in Linux with Xvfb

CI/CD 在 Linux 環境上執行 Cypress 時會需要先安裝 Xvfb 但在執行上有時候會遇到問題,可以透過以下指令排解。

  • ps -ef | grep Xvfb
  • ps -ef | grep Xvfb | grep -v grep | awk '{print $2}' | xargs kill -9

上一篇
Hack 網頁從按鈕開始!HTML/CSS/JS Debug 技巧 (29)
系列文
前端三分鐘 X 從把妹角度理解前後端如何和平相處30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言